﻿// Coroutine.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include<boost/asio/co_spawn.hpp>
#include<boost/asio/detached.hpp>
#include<boost/asio/io_context.hpp>
#include<boost/asio/ip/tcp.hpp>
#include<boost/asio/signal_set.hpp>
#include<boost/asio/write.hpp>



#include <iostream>
using boost::asio::ip::tcp;
using boost::asio::awaitable;//让携程可等待;a-异步等待
using boost::asio::co_spawn;//协程必需
using boost::asio::detached;
using boost::asio::use_awaitable;//关键字使协程可等待
namespace this_coro = boost::asio::this_coro;//返回当前协程的所使用的调度器
awaitable<void> echo(tcp::socket socket) {
    try {
        char data[1024];
        for (;;) {
           std::size_t n= co_await socket.async_read_some(boost::asio::buffer(data),use_awaitable);
           co_await async_write(socket, boost::asio::buffer(data, n), use_awaitable);
        }
    }
    catch (std::exception& e) {
        std::cout << "echo exception is:" << e.what() << std::endl;
    }
}
awaitable<void> listener() {
    auto excutor = co_await this_coro::executor;//通过co_await异步查询调度器,查不到就挂起,主线程继续往下走
    //协程找到调度器io_c,被唤醒,执行下列操作
    tcp::acceptor acceptor(excutor, { tcp::v4(),10086 });//在协程内部给调度器(这里是io_context)注册监听事件,防止run()后直接退出
    for (;;) {//顺序性编程特性体现
        tcp::socket socket = co_await acceptor.async_accept(use_awaitable);
        //协程到这里异步等待,主线程发现协程被挂起,回收资源进行之后的操作,这里是执行listenner之后的操作
        //真的等到这个连接后协程被唤醒,接着往下走
        co_spawn(excutor, echo(std::move(socket)), detached);//通过移动构造方式把socket传给函数,也可以传引用   或者值(相当于一个副本)
        //此时外部的socket因为被移动了也就失去效果了

         

    }
}
int main()
{
    try {
        boost::asio::io_context io_context;
        boost::asio::signal_set signals(io_context, SIGINT, SIGTERM);
        signals.async_wait([&](auto, auto) {
            io_context.stop();
            });
        co_spawn(io_context, listener(), detached);//co_spawn启动一个携程,第一个参数指明协程的调度器是谁也就是io_c,第二个是协程的任务,随时接受这个函数的返回
        //co_spawn(io_context, listener(), detached);再开一个协程
        //detached不是说主线程会在这里等,而是比如上面再开一个,第一个协程挂起后,执行第二个协程,两者是串行的;
        io_context.run();
    }
    catch (std::exception& e) {
        std::cout << "Exception is:" << e.what() << std::endl;
    }
    std::cout << "Hello World!\n";
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件
